import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';

import 'package:dbio_utils/generic_filehandle/generic_file_handle.dart';

class RemoteFile extends GenericFileHandle {
  String _url;

  int _length;

  RemoteFile({
    String url,
  }) {
    _url = url;
  }

  @override
  Future<Uint8List> read(int offset, int length, [int position = 0, String encoding]) async {
    Map headers = {};
    String _range;
    if (length < double.infinity) {
      headers['range'] = "bytes=$position-${position + length}";
    } else if (length == double.infinity && position != 0) {
      headers['range'] = "bytes=$position-";
    }
    List<int> bytes = await fetch(this._url, headers);
    return bytes;
  }

  @override
  Future<Uint8List> readFile({String encoding}) async {
    Map headers = {};
    List<int> bytes = await fetch(this._url, headers);
    return bytes;
  }

  @override
  int get size => _length;

  Future<List<int>> fetch(
    String url,
    Map header,
  ) async {
    HttpClient client = HttpClient();
    List<int> bytes;
    try {
      HttpClientRequest request = await client.getUrl(Uri.parse(url));
      header.forEach((key, value) {
        request.headers.set(key, value);
      });
      HttpClientResponse response = await request.close();

      if (response.statusCode == 200) {
        // String content = await response.transform(utf8.decoder).join('');
        bytes = (await response.toList())[0];

        // try to parse out the size of the remote file
        final res = response.headers.value('content-range');
        RegExp regExp = RegExp(r'/\/(\d+)$/');
        final sizeMatch = regExp.stringMatch(res ?? '');
        if (sizeMatch != null) {
          _length = int.parse(sizeMatch);
        }
      }
    } catch (e) {
      print(e);
    } finally {
      client.close();
    }
    return bytes;
  }

  @override
  Future<String> readAsString({Encoding encoding}) {
    throw UnimplementedError();
  }

  @override
  Stream<List<int>> openStream([int start, int end]) {
    throw UnimplementedError();
  }

  @override
  Future<Map> stat() async {
    return {
      'size': _length,
    };
  }
}
